﻿using System;
using System.Text;
using XAMSDK.Types;
using System.Collections.Generic;

namespace XAMSDK
{

    public class XSystem : XAMObject
    {
        internal XSystemHandle handle;

        #region Constructors
        internal XSystem(XSystemHandle h)
        {
            handle = h;
            xamHandle = (XAMHandle)handle;
        }
        #endregion

        #region Factory Methods

        /// <summary>
        /// Open an existing XSet that resides on the XSystem
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be opened</param>
        /// <param name="mode">the mode that the XSet is to be opened in</param>
        /// <returns>an XSet instance</returns>
        public XSet OpenXSet(XUID xuid, string mode)
        {
            XSetHandle xseth;
            int retval = Imports.XSystem_OpenXSet(handle, xuid, mode, out xseth);
            Helpers.CheckAndThrow(retval, "XSystem_OpenXSet");

            XSet xset = new XSet(xseth);
            factoryObjects.Add(xset);
            xset.xsys = this;

            return xset;
        }

        /// Asynchronously open an existing XSet that resides on the XSystem
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be opened</param>
        /// <param name="mode">the mode that the XSet is to be opened in</param>
        /// <param name="opID">a user defined handle for the async operation</param>
        /// <param name="callback">the callback to be called on operation completion</param>
        /// <returns>an XSet instance</returns>
        public XAsyncObject OpenXSet(XUID xuid, string mode, long opID, XAsyncCallback callback)
        {
            XAsyncHandle xah;

            int retval = Imports.XSystem_AsyncOpenXSet(handle, xuid, mode, opID, callback, out xah);
            Helpers.CheckAndThrow(retval, "XSystem_AsyncOpenXSet");

            return new XAsyncObject(xah, XAsyncType.XSetOpen);
        }

        /// <summary>
        /// Create a new XSet on the existing XSystem
        /// </summary>
        /// <returns>an XSet instance</returns>
        public XSet CreateXSet()
        {
            XSetHandle xseth;
            int retval = Imports.XSystem_CreateXSet(handle, Constants.XSET_MODE_MODIFY, out xseth);
            Helpers.CheckAndThrow(retval, "XSystem_CreateXSet");

            XSet xset = new XSet(xseth);
            factoryObjects.Add(xset);
            xset.xsys = this;

            return xset;
        }

        /// <summary>
        /// Create an XQuery on the current XSystem
        /// </summary>
        /// <param name="queryStr">the query string (must be XAM QL compliant)</param>
        /// <param name="inBinding">the binding status of the Query command field</param>
        /// <returns></returns>
        public XQuery CreateXQuery(string queryStr, bool inBinding)
        {
            if (!QueryLevel1)
                throw (new XAMException(1032, "CreateXQuery"));

            return new XQuery(handle, CreateXSet(), queryStr, inBinding);
        }

        /// <summary>
        /// Convenience method which creates an XQuery on the current XSystem with a binding Query command field
        /// </summary>
        /// <param name="queryStr">the query string (must be XAM QL compliant)</param>
        /// <returns></returns>
        public XQuery CreateXQuery(string queryStr) { return CreateXQuery(queryStr, true); }

        /// <summary>
        /// Create a copy of an existing XSet
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be copied</param>
        /// <param name="mode">the mode that the resulting copy is opened in</param>
        /// <returns>an XSet instance that contains the copy of the XSet</returns>
        public XSet CopyXSet(XUID xuid, string mode)
        {
            XSetHandle xseth;
            int retval = Imports.XSystem_CopyXSet(handle, xuid, mode, out xseth);
            Helpers.CheckAndThrow(retval, "XSystem_CopyXSet");

            XSet xset = new XSet(xseth);
            factoryObjects.Add(xset);
            xset.xsys = this;

            return xset;
        }

        /// <summary>
        /// Asynchronously create a copy of an existing XSet
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be copied</param>
        /// <param name="mode">the mode that the resulting copy is opened in</param>
        /// <param name="opID">a user defined handle for the async operation</param>
        /// <param name="callback">the callback to be called on operation completion</param>
        /// <returns>an XSet instance that contains the copy of the XSet</returns>
        public XAsyncObject CopyXSet(XUID xuid, string mode, long opID, XAsyncCallback callback)
        {
            XAsyncHandle xah;

            int retval = Imports.XSystem_AsyncCopyXSet(handle, xuid, mode, opID, callback, out xah);
            Helpers.CheckAndThrow(retval, "XSystem_AsyncCopyXSet");

            return new XAsyncObject(xah, XAsyncType.XSetCopy);
        }

        #endregion

        #region Methods
        /// <summary>
        /// Populate the PolicyList of the base XAMObject with the relevant XSystem policies
        /// </summary>
        public override void GetPolicies()
        {
            if (policyList.Count == 0)
            {
                GetPolicies(XPolicyType.Management, Constants.XSYSTEM_MANAGEMENT_POLICY_LIST);
                GetPolicies(XPolicyType.AutoDelete, Constants.XSYSTEM_AUTODELETE_POLICY_LIST);
                GetPolicies(XPolicyType.Shred, Constants.XSYSTEM_SHRED_POLICY_LIST);
                GetPolicies(XPolicyType.Storage, Constants.XSYSTEM_STORAGE_POLICY_LIST);
                GetPolicies(XPolicyType.RetentionEnabled, Constants.XSYSTEM_RETENTION_ENABLED_POLICY_LIST);
                GetPolicies(XPolicyType.RetentionDuration, Constants.XSYSTEM_RETENTION_DURATION_POLICY_LIST);
             }
        }

        /// <summary>
        /// Delete an XSet from this XSystem instance
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be deleted</param>
        public void DeleteXSet(XUID xuid)
        {
            int retval = Imports.XSystem_DeleteXSet(handle, xuid);
            Helpers.CheckAndThrow(retval, "XSystem_DeleteXSet");
        }

        /// <summary>
        /// Close the current XSystem instance - will be called by the Dispose method.
        /// </summary>
        protected override void Close()
        {
            if (!disposed)
            {
                int retval = Imports.XSystem_Close(handle);
                Helpers.CheckAndThrow(retval, "XSystem_Close");
            }
        }

        /// <summary>
        /// Authenticate this XSystem instance using the credentials supplied
        /// </summary>
        /// <param name="credentials"></param>
        public void Authenticate(string credentials)
        {
            XStreamHandle authStream = 0;
            byte[] byteStr = Helpers.encoder.GetBytes(credentials);

            int retval = Imports.XSystem_Authenticate(handle, credentials, (long) credentials.Length, out authStream);
            Helpers.CheckAndThrow(retval, "XSystem_Authenticate");

            retval = Imports.XStream_Close(authStream);
            Helpers.CheckAndThrow(retval, "XSystem_Authenticate - stream closure");
        }

        /// <summary>
        /// Authenticate this XSystem instance using ANONYMOUS credentials.
        /// </summary>
        public void Authenticate() { Authenticate("ANONYMOUS\0ANONYMOUS"); }

        /// <summary>
        /// Abandon the current XSystem. Should only be used if it cannot be grecfully closed / disposed.
        /// </summary>
        public void Abandon()
        {
            int retval = Imports.XSystem_Abandon(handle);
            Helpers.CheckAndThrow(retval, "XSystem_Abandon");
        }

        /// <summary>
        /// Is an XSet on this XSystem currently under retention?
        /// </summary>
        /// <param name="xuid">the XUID of an XSet on the XSystem</param>
        /// <returns>the retention status</returns>
        public bool XSetRetained(XUID xuid)
        {
            bool retained;

            int retval = Imports.XSystem_IsXSetRetained(handle, xuid, out retained);
            Helpers.CheckAndThrow(retval, "XSystem_Abandon");

            return retained;
        }

        /// <summary>
        /// Set a hold on an XSet on this XSystem.
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to be placed under hold</param>
        /// <param name="holdID">the ID / secret to be used for the hold</param>
        public void HoldXSet(XUID xuid, XString holdID)
        {
            int retval = Imports.XSystem_HoldXSet(handle, xuid, holdID);
            Helpers.CheckAndThrow(retval, "XSystem_HoldXSet");
        }

        /// <summary>
        /// Release an existing hold on an XSet on this XSystem
        /// </summary>
        /// <param name="xuid">the XUID of the XSet from which the hold should be removed</param>
        /// <param name="holdID">the ID / secret of the hold</param>
        public void ReleaseXSet(XUID xuid, XString holdID)
        {
            int retval = Imports.XSystem_ReleaseXSet(handle, xuid, holdID);
            Helpers.CheckAndThrow(retval, "XSystem_ReleaseXSet");
        }

        /// <summary>
        /// Is an XSet on this XSystem accessible?
        /// </summary>
        /// <param name="xuid">the XUID of the XSet to determine the accessibility of</param>
        /// <param name="mode">the access permission to check for accesibility on the XSet</param>
        /// <returns></returns>
        public bool XSetAccessible(XUID xuid, long mode)
        {
            bool accessible;

            int retval = Imports.XSystem_AccessXSet(handle, xuid, mode, out accessible);
            Helpers.CheckAndThrow(retval, "XSystem_AccessXSet");

            return accessible;
        }

        /// <summary>
        /// Get the AccessTime for an XSet on this XSystem
        /// </summary>
        /// <param name="xuid">the XUID of the XSet</param>
        /// <returns>the last AccessTime of the XSet</returns>
        public DateTime XSetAccessTime(XUID xuid)
        {
            XString accessTime = new XString();

            int retval = Imports.XSystem_GetXSetAccessTime(handle, xuid, accessTime);
            Helpers.CheckAndThrow(retval, "XSystem_GetXSetAccessTime");

            return DateTime.Parse(accessTime);
        }
        #endregion

        #region Properties

        /// <summary>
        /// The vendor identity of the XSystem instance.
        /// </summary>
        public XString Identity { get { XString value = new XString(); GetField(Constants.XSYSTEM_IDENTITY, value); return value; } }

        /// <summary>
        /// The default SASL mechanism for the connected XSystem.
        /// </summary>
        public XString SASLDefault { get { XString value = new XString(); GetField(Constants.XSYSTEM_SASL_DEFAULT, value); return value; } }

        /// <summary>
        /// The ID used during authentication.
        /// </summary>
        public XString Authentication { get { XString value = new XString(); GetField(Constants.XSYSTEM_AUTH_IDENTITY_AUTHENTICATION, value); return value; } }

        /// <summary>
        /// The id used during authorization.
        /// </summary>
        public XString Authorization { get { XString value = new XString(); GetField(Constants.XSYSTEM_AUTH_IDENTITY_AUTHORIZATION, value); return value; } }

        /// <summary>
        /// The current time of the XSystem instance.
        /// </summary>
        public DateTime Time { get { DateTime value = new DateTime(); GetField(Constants.XSYSTEM_TIME, out value); return value; } }

        /// <summary>
        /// The time that Authorization expires.
        /// </summary>
        public DateTime Expiration { get { DateTime value = new DateTime(); GetField(Constants.XSYSTEM_AUTH_EXPIRATION, out value); return value; } }

        /// <summary>
        /// The maximum number of fields that may be created in an XSet.
        /// </summary>
        public long MaxFields { get { long value = 0; GetField(Constants.XSYSTEM_MAX_FIELDS, out value); return value; } }

        /// <summary>
        /// The maximum size of an XStream.
        /// </summary>
        public long MaxStreamSize { get { long value = 0; GetField(Constants.XSYSTEM_MAX_SIZE_OF_XSTREAM, out value); return value; } }

        /// <summary>
        /// Does the XSystem xsupport commits of running queries?
        /// </summary>
        public bool Commit { get { bool value = false; GetField(Constants.XSYSTEM_JOB_COMMIT_SUPPORTED, out value); return value; } }

        /// <summary>
        /// Does the XSystem support queries that continue while disconnected?
        /// </summary>
        public bool Continuance { get { bool value = false; GetField(Constants.XSYSTEM_JOB_QUERY_CONTINUANCE_SUPPORTED, out value); return value; } }

        /// <summary>
        /// Does the XSystem support Level 1 Query?
        /// </summary>
        public bool QueryLevel1 { get { bool value = false; GetField(Constants.XSYSTEM_JOB_QUERY_LEVEL1_SUPPORTED, out value); return value; } }

        /// <summary>
        /// Does the XSystem support Level w Query?
        /// </summary>
        public bool QueryLevel2 { get { bool value = false; GetField(Constants.XSYSTEM_JOB_QUERY_LEVEL2_SUPPORTED, out value); return value; } }

        /// <summary>
        /// Does the XSystem support Auto Delete?
        /// </summary>
        public bool AutoDelete { get { bool value = false; GetField(Constants.XSYSTEM_AUTODELETE, out value); return value; } }

        /// <summary>
        /// Does the XSystem support Shredding?
        /// </summary>
        public bool Shred { get { bool value = false; GetField(Constants.XSYSTEM_SHRED, out value); return value; } }

        /// <summary>
        /// Has this XSystem instance been authenticated?
        /// </summary>
        public bool Authenticated { get {return ContainsField(Constants.XSYSTEM_AUTH_IDENTITY_AUTHENTICATION); } }
        #endregion
    }
}